package invoke

import (
    "context"
    "encoding/base64"
    "encoding/json"
    "errors"
    "fcs-api/src/app/model"
    "fmt"
    "strconv"

    "git.inspur.com/x/common/loggerfactory"
    cloudevents "github.com/cloudevents/sdk-go/v2"
)

var logger = loggerfactory.GetLogger()

type Message struct {
    Body string `json:"body"`
}

func CallFunction(info *model.CallMessage) (*model.Invoke, error) {

    c, err := cloudevents.NewDefaultClient()
    if err != nil {
        _ = logger.Error("CallFunction: failed to create client, %v", err)
        return nil, err
    }

    url := fmt.Sprintf("%s/sync/%s/%s/%s.%s", info.Host, info.Id, info.GroupName, info.FunctionName, info.VersionName)
    event := cloudevents.NewEvent(cloudevents.VersionV1)
    event.SetType(model.InvokeEventType)
    event.SetSource(model.InvokeEventSource)
    if info.Event != "" {
        if err := event.SetData(cloudevents.ApplicationJSON, Messages(info.Event)); err != nil {
            _ = logger.Error("CallFunction: failed set data %v", err)
            return nil, err
        }
    }

    // 设置请求认证 header，通过 iam client 生成的 token
    token, err := model.Base.KeycloakClient.ClientToken()
    if err != nil {
        _ = logger.Error("CallFunction: failed to get iam client token, %v", err)
        return nil, err
    }
    event.SetExtension("Authorization", "bearer "+token.AccessToken)

    ctx := cloudevents.ContextWithTarget(context.Background(), url)
    // 发送cloud event请求并获取结果
    invokeData, result := c.Request(ctx, event)
    if !cloudevents.IsACK(result) {
        _ = logger.Error("CallFunction: failed to send, %v", result)
        return nil, errors.New(fmt.Sprintf("invoke error %s", result.Error()))
    }
    invoke := &model.Invoke{}
    if invokeData.Extensions() == nil {
        _ = logger.Error("CallFunction: receive data is null, %v", result)
        return nil, errors.New(fmt.Sprintf("receive data is nil"))
    }
    extensionStr, _ := json.Marshal(invokeData.Extensions())
    logger.Debug("call url is %s, result is %s", url, string(extensionStr))
    if invokeData.Extensions()["fcrequestid"] == nil || invokeData.Extensions()["fcrequestid"] == "" {
        _ = logger.Error("CallFunction: FcRequestId is null, %v", result)
        return nil, errors.New(fmt.Sprintf("receive data is nil"))
    }
    invoke.Id = invokeData.Extensions()["fcrequestid"].(string)
    if memory, err := strconv.Atoi(invokeData.Extensions()["fcmaxmemoryusage"].(string)); err != nil {
        _ = logger.Error("CallFunction: get memory data error, %v", err)
    } else {
        invoke.MemoryUsage = memory
    }
    if decodeBytes, err := base64.StdEncoding.DecodeString(invokeData.Extensions()["fclog"].(string)); err != nil {
        _ = logger.Error("CallFunction: get log data error, %v", err)
    } else {
        invoke.Log = string(decodeBytes)
    }
    if rtDuration, err := strconv.Atoi(invokeData.Extensions()["fcduration"].(string)); err != nil {
        _ = logger.Error("CallFunction: get RuntimeDuration data error, %v", err)
    } else {
        invoke.RuntimeDuration = rtDuration
    }
    if status, err := strconv.Atoi(invokeData.Extensions()["fcstatus"].(string)); err != nil {
        _ = logger.Error("CallFunction: get status code data error, %v", err)
    } else {
        invoke.ReturnCode = status
    }
    invoke.Checksum = invokeData.Extensions()["fccodechecksum"].(string)
    invoke.ReturnMsg = string(invokeData.Data())
    return invoke, nil
}

func Messages(body string) interface{} {
    // try to marshal the body into an interface.
    var obj map[string]*json.RawMessage
    if err := json.Unmarshal([]byte(body), &obj); err != nil {
        //default to a wrapped message.
        return Message{Body: body}
    }
    return obj
}
